feat: publish multi-arch Docker image on release#254
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
Dockerfile (1)
10-13: Add artifact integrity verification before extraction.Lines 10-13 download and unpack a remote archive without validating a checksum/signature. Prefer verifying the release checksum before
tar -xJf.Suggested hardening
&& curl -fsSL -o /tmp/tower.tar.xz \ "https://github.com/tower/tower-cli/releases/download/v${VERSION}/tower-${ARCH}-unknown-linux-musl.tar.xz" \ +&& curl -fsSL -o /tmp/tower.tar.xz.sha256 \ + "https://github.com/tower/tower-cli/releases/download/v${VERSION}/tower-${ARCH}-unknown-linux-musl.tar.xz.sha256" \ +&& sha256sum -c /tmp/tower.tar.xz.sha256 \ && mkdir -p /out \ && tar -xJf /tmp/tower.tar.xz -C /out --strip-components=1 \🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Dockerfile` around lines 10 - 13, Add integrity verification for the downloaded artifact before extraction: after downloading /tmp/tower.tar.xz (the curl step that uses VERSION and ARCH) fetch the corresponding checksum or signature (from the release assets or a .sha256/.asc URL), verify it (e.g., compare computed sha256sum of /tmp/tower.tar.xz against the fetched checksum or import and verify a GPG signature), and fail the build if verification fails; only then run tar -xJf /tmp/tower.tar.xz -C /out --strip-components=1. Ensure the verification step is atomic in the Dockerfile so that invalid artifacts never get extracted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/publish-docker.yml:
- Around line 35-43: The build strips a leading "v" into VERSION but the
Dockerfile expects the original tag (v-prefixed or not) when constructing the
release download URL, causing mismatches; stop relying on stripped VERSION for
the download and pass the original TAG through to the Docker build. Modify the
workflow to set VERSION="${TAG#v}" as before for image tagging but also pass the
unmodified TAG as a separate build-arg (e.g., RELEASE_TAG or FULL_TAG) in the
docker buildx build invocation (alongside --build-arg VERSION="$VERSION"), and
update the Dockerfile to use that RELEASE_TAG when forming the /download/... URL
(instead of v${VERSION}) so the exact release tag format is preserved.
In `@Dockerfile`:
- Around line 16-18: The final image stage uses
gcr.io/distroless/static-debian12 and sets ENTRYPOINT to /usr/local/bin/tower
but does not set a non-root user; update the final stage of the Dockerfile to
add a USER directive (e.g., a non-root UID/GID like 1000 or a created user) so
the container does not run as root, and ensure /usr/local/bin/tower and any
required directories have appropriate ownership/permissions for that UID before
switching users (adjust COPY/permission steps as needed).
---
Nitpick comments:
In `@Dockerfile`:
- Around line 10-13: Add integrity verification for the downloaded artifact
before extraction: after downloading /tmp/tower.tar.xz (the curl step that uses
VERSION and ARCH) fetch the corresponding checksum or signature (from the
release assets or a .sha256/.asc URL), verify it (e.g., compare computed
sha256sum of /tmp/tower.tar.xz against the fetched checksum or import and verify
a GPG signature), and fail the build if verification fails; only then run tar
-xJf /tmp/tower.tar.xz -C /out --strip-components=1. Ensure the verification
step is atomic in the Dockerfile so that invalid artifacts never get extracted.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b4a8d39b-d207-41ee-9946-41be0fe7c466
📒 Files selected for processing (4)
.github/workflows/publish-docker.yml.github/workflows/release.ymlDockerfiledist-workspace.toml
| VERSION="${TAG#v}" | ||
| TAGS=(--tag "ghcr.io/tower/tower-cli:$VERSION") | ||
| if [ "$PRERELEASE" != "true" ]; then | ||
| TAGS+=(--tag "ghcr.io/tower/tower-cli:latest") | ||
| fi | ||
| docker buildx build \ | ||
| --platform linux/amd64,linux/arm64 \ | ||
| --build-arg VERSION="$VERSION" \ | ||
| "${TAGS[@]}" \ |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In cargo-dist release manifests, is announcement_tagguaranteed to include avprefix, or can it be plain semver like1.2.3?
💡 Result:
In cargo-dist release manifests, announcement_tag is not guaranteed to include a v prefix and can be plain semver like 1.2.3. The field announcement_tag in DistManifest is an Option described as "The (git) tag associated with this announcement". Cargo-dist's tag parsing is permissive: it matches git tags using the regex ^[0-9]+.[0-9]+.[0-9]+*, which captures plain semver versions like 1.2.3 without a v prefix. Official docs and examples emphasize conventional formats like v1.0.0 or my-app-v1.0.0, but a key PR (#346) explicitly made parsing more flexible: "The 'v' prefix on a version is now optional". Supported formats include: - v1.0.0 - 1.0.0 - package-name-v1.0.0 - package-name-1.0.0 - package-name/v1.0.0 - package-name/1.0.0 Thus, plain semver like 1.2.3 is valid, and announcement_tag will reflect whatever git tag triggered the release or was passed via --tag.
Citations:
- 1: https://docs.rs/cargo-dist-schema/latest/cargo_dist_schema/struct.DistManifest.html
- 2: https://axodotdev.github.io/cargo-dist/book/workspaces/cargo-release-guide.html
- 3: https://github.com/axodotdev/cargo-dist/blob/main/.github/workflows/release.yml
- 4: feat: support slash as tag separator axodotdev/cargo-dist#346
- 5: https://github.com/axodotdev/cargo-dist/releases/tag/v0.17.0
🏁 Script executed:
# First, find and read the Dockerfile to see the release URL construction
fd -t f "Dockerfile" | head -20Repository: tower/tower-cli
Length of output: 69
🏁 Script executed:
# Check recent git tags to see what format the project uses
git tag -l | head -20Repository: tower/tower-cli
Length of output: 41
🏁 Script executed:
# Read the Dockerfile to inspect the release URL construction
fd -t f "Dockerfile" -x cat -nRepository: tower/tower-cli
Length of output: 851
🏁 Script executed:
cat -n .github/workflows/publish-docker.yml | sed -n '30,50p'Repository: tower/tower-cli
Length of output: 739
🏁 Script executed:
git tag --list | tail -20Repository: tower/tower-cli
Length of output: 41
Avoid v stripping/reconstruction mismatch for release asset lookup.
Line 35 strips v from TAG (the announcement_tag from cargo-dist), while the Dockerfile hardcodes v${VERSION} in the release download URL (Dockerfile line 11). Since cargo-dist allows plain semver tags like 1.2.3 (without v prefix), a release created with a non-prefixed tag would cause the Docker build to fail—it would request /download/v1.2.3/ when assets are actually at /download/1.2.3/.
Pass the original tag value to the Dockerfile so it uses the exact release tag format:
Suggested fix
- VERSION="${TAG#v}"
+ VERSION="${TAG#v}"
+ RELEASE_TAG="$TAG"
TAGS=(--tag "ghcr.io/tower/tower-cli:$VERSION")
@@
docker buildx build \
--platform linux/amd64,linux/arm64 \
--build-arg VERSION="$VERSION" \
+ --build-arg RELEASE_TAG="$RELEASE_TAG" \
"${TAGS[@]}" \
--push .In Dockerfile:
-ARG VERSION
+ARG VERSION
+ARG RELEASE_TAG
@@
- "https://github.com/tower/tower-cli/releases/download/v${VERSION}/tower-${ARCH}-unknown-linux-musl.tar.xz" \
+ "https://github.com/tower/tower-cli/releases/download/${RELEASE_TAG}/tower-${ARCH}-unknown-linux-musl.tar.xz" \🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/publish-docker.yml around lines 35 - 43, The build strips
a leading "v" into VERSION but the Dockerfile expects the original tag
(v-prefixed or not) when constructing the release download URL, causing
mismatches; stop relying on stripped VERSION for the download and pass the
original TAG through to the Docker build. Modify the workflow to set
VERSION="${TAG#v}" as before for image tagging but also pass the unmodified TAG
as a separate build-arg (e.g., RELEASE_TAG or FULL_TAG) in the docker buildx
build invocation (alongside --build-arg VERSION="$VERSION"), and update the
Dockerfile to use that RELEASE_TAG when forming the /download/... URL (instead
of v${VERSION}) so the exact release tag format is preserved.
| FROM gcr.io/distroless/static-debian12 | ||
| COPY --from=fetch /out/tower /usr/local/bin/tower | ||
| ENTRYPOINT ["/usr/local/bin/tower"] |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does gcr.io/distroless/static-debian12 run as root by default, and what is the recommended non-root UID/GID for distroless static images?
💡 Result:
Yes, gcr.io/distroless/static-debian12 (the latest tag) runs as root (UID 0) by default. The image has variants including :nonroot, which sets the default user to the nonroot user with UID/GID 65532. The recommended non-root UID/GID for distroless static images is 65532:65532, corresponding to the built-in nonroot user. This is the standard unprivileged user provided in all distroless images for running containers securely without root privileges. Users can also use nobody (UID/GID 65534), but nonroot is the specifically recommended one via the :nonroot tag. To use it, either pull gcr.io/distroless/static-debian12:nonroot or add USER nonroot (or USER 65532:65532) in your Dockerfile when basing on the root variant.
Citations:
- 1: https://pkg.go.dev/github.com/GoogleContainerTools/distroless
- 2: https://github.com/googlecontainertools/distroless
- 3: https://stackoverflow.com/questions/73568034/nobody-nonroot-user-in-distroless-images
- 4: Do not use bug UID/GID GoogleContainerTools/distroless#1248
- 5: Document nonroot and nobody user/group GoogleContainerTools/distroless#443
- 6: https://software.codidact.com/posts/291574?sort=score
- 7: base-nossl-debian12:nonroot uses root user GoogleContainerTools/distroless#2045
Run the container as non-root.
Line 16 uses gcr.io/distroless/static-debian12, which defaults to running as root (UID 0). Lines 17–18 do not set a user, so the container will run with root privileges. Add a USER directive in the final stage to run as the recommended non-root user.
Suggested fix
FROM gcr.io/distroless/static-debian12
COPY --from=fetch /out/tower /usr/local/bin/tower
+USER 65532:65532
ENTRYPOINT ["/usr/local/bin/tower"]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| FROM gcr.io/distroless/static-debian12 | |
| COPY --from=fetch /out/tower /usr/local/bin/tower | |
| ENTRYPOINT ["/usr/local/bin/tower"] | |
| FROM gcr.io/distroless/static-debian12 | |
| COPY --from=fetch /out/tower /usr/local/bin/tower | |
| USER 65532:65532 | |
| ENTRYPOINT ["/usr/local/bin/tower"] |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@Dockerfile` around lines 16 - 18, The final image stage uses
gcr.io/distroless/static-debian12 and sets ENTRYPOINT to /usr/local/bin/tower
but does not set a non-root user; update the final stage of the Dockerfile to
add a USER directive (e.g., a non-root UID/GID like 1000 or a created user) so
the container does not run as root, and ensure /usr/local/bin/tower and any
required directories have appropriate ownership/permissions for that UID before
switching users (adjust COPY/permission steps as needed).
575fdd5 to
fc4e07d
Compare
Adds a
Dockerfileand apublish-dockerreusable workflow wired into the cargo-dist release pipeline. On each tagged release it pulls the prebuilt musl binaries and pushes a distroless-based image toghcr.io/tower/tower-cliforlinux/amd64andlinux/arm64.Also adds
aarch64-unknown-linux-muslto cargo-dist targets (the wheel build already uses it, so no new toolchain requirements) and drops the vestigialannouncejob, which had no steps.Summary by CodeRabbit
amd64andarm64platforms.